package org.tech4j.captcha.graphical.service;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.function.TriFunction;
import org.tech4j.captcha.api.CaptchaStore;
import org.tech4j.captcha.api.CaptchaStoreFactory;
import org.tech4j.captcha.api.model.CaptchaResponse;
import org.tech4j.captcha.graphical.config.CaptchaGraphicalConfig;
import org.tech4j.captcha.graphical.model.CheckCaptchaEntity;
import org.tech4j.captcha.graphical.model.GraphicalCaptchaEntity;
import org.tech4j.captcha.graphical.model.GraphicalResult;
import org.tech4j.captcha.graphical.model.VerifyCaptchaEntity;
import org.tech4j.common.spi.Join;

import java.util.Objects;
import java.util.Properties;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @program: tech4j
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/4 20:45
 **/
@Join
public class RateLimiterCaptchaService implements CaptchaService {
    protected static String REDIS_CAPTCHA_KEY = "RUNNING:CAPTCHA:%s";

    protected static String REDIS_SECOND_CAPTCHA_KEY = "RUNNING:CAPTCHA:second-%s";
    private CaptchaGraphicalConfig defaultConfig = new CaptchaGraphicalConfig();

    @Override
    public void init(CaptchaGraphicalConfig config) {
        defaultConfig = config;
        defaultConfig.getWaterMarkConfig().loadWaterMarkFont();
        CaptchaGraphicalConfig.FontConfig fontConfig = defaultConfig.getFontConfig();
        defaultConfig.getClickWordConfig().loadClickWordFont(fontConfig.getFontSize(), fontConfig.getFontStyle());
    }

    @Override
    public CaptchaResponse getCaptcha(GraphicalCaptchaEntity captchaEntity) {
        doRateLimiter();
        Function<CaptchaGraphicalConfig, GraphicalResult> result = captchaEntity.getResult();
        GraphicalResult graphicalResult = result.apply(defaultConfig);
        CaptchaStore captchaStore = CaptchaStoreFactory.newInstance(captchaEntity.getStoreType());
        captchaStore.storeCaptcha(buildKey(graphicalResult.getToken()), graphicalResult.getPointValueJson(), captchaEntity.getExpireAt(), captchaEntity.getTimeUnit());
        graphicalResult.setPointValueJson(null);
        return CaptchaResponse.ofSuccess(graphicalResult);
    }

    @Override
    public CaptchaResponse checkCaptcha(CheckCaptchaEntity captchaEntity) {
        doRateLimiter();
        String key = buildKey(captchaEntity.getKey());
        CaptchaStore captchaStore = CaptchaStoreFactory.newInstance(captchaEntity.getStoreType());
        String value = captchaStore.readCaptcha(key);
        if (StringUtils.isBlank(value)) {
            return CaptchaResponse.ofFailed("captcha_invalid", "验证码已失效，请重新获取");
        }
        // 验证码只用一次
        captchaStore.removeCaptcha(key);
        TriFunction<CaptchaGraphicalConfig, CheckCaptchaEntity, String, CaptchaResponse> captchaCheckFunction = captchaEntity.getCaptchaCheck();
        CaptchaResponse response = captchaCheckFunction.apply(defaultConfig, captchaEntity, value);
        if (response.isFailed()) {
            return response;
        }
        // 二次验证
        captchaStore.storeCaptcha(buildSecondKey((String) response.getData()), captchaEntity.getKey(), captchaEntity.getExpireAt(), captchaEntity.getTimeUnit());
        response.setData(null);
        return response;
    }

    @Override
    public CaptchaResponse verification(VerifyCaptchaEntity captchaEntity) {
        doRateLimiter();
        String key = buildSecondKey(captchaEntity.getKey());
        CaptchaStore captchaStore = CaptchaStoreFactory.newInstance(captchaEntity.getStoreType());
        String value = captchaStore.readCaptcha(key);
        if (StringUtils.isBlank(value)) {
            return CaptchaResponse.ofFailed("captcha_invalid", "验证码已失效，请重新获取");
        }
        captchaStore.removeCaptcha(key);
        return CaptchaResponse.ofSuccess();
    }

    private void doRateLimiter() {
        // 限流判断
        CaptchaGraphicalConfig.RateLimiterConfig rateLimiterConfig = defaultConfig.getRateLimiterConfig();
        if (Objects.nonNull(rateLimiterConfig) && Boolean.TRUE.equals(rateLimiterConfig.getRequestRateLimiterEnabled())) {
            // 限流操作
        }
    }

    private String buildKey(String key) {
        return String.format(REDIS_CAPTCHA_KEY, key);
    }

    private String buildSecondKey(String key) {
        return String.format(REDIS_SECOND_CAPTCHA_KEY, key);
    }
}
